<?php
/**
 * 云账户
 * */

namespace App\Lib;

use Illuminate\Support\Facades\Log;

class CloudAccountRsa
{
    public $requestData = [];// 接收参数

    private static $instance;
    protected $crypt3DesEcb;

    // 定义子类 api
    protected $subApi = [
        'orderRealTime' => '/api/payment/v1/order-realtime',
        'verifyBankCard' => '/authentication/verify-bankcard-three-factor',
        'getH5Url' => '/api/aic/new-economy/h5/v1/h5url',
        'queryStatus' => '/api/aic/new-economy/h5/v1/status',
        'queryOrderStatus' => '/api/payment/v1/query-realtime-order',
        'userContract' => '/api/sign/v1/user/contract',
    ];

    protected $mess = '';
    protected $timestamp = 0;

    // 配置
    public $selfConfig = [];
    // 构造
    private function __construct($type)
    {
        if (env('APP_ENV') != 'product') {
            if($type == 1){
                $this->selfConfig = config('cloud_account_dev') ?? [];
            }else{
                $this->selfConfig = config('cloud_account_large_dev') ?? [];
            }
        } else {
            if($type == 1){
                $this->selfConfig = config('cloud_account') ?? [];
            }else{
                $this->selfConfig = config('cloud_account_large') ?? [];
            }
        }


        $this->crypt3DesEcb = new Crypt3DesEcb($this->selfConfig['appSecret']);

        $this->mess = uniqid();
        $this->timestamp = time();
    }

    /**返回实例
     * @param $type
     * @return CloudAccountRsa
     */
    static public function getInstance($type)
    {
        self::$instance = new self($type);

        return self::$instance;
    }
    //数组转换为字符串

    public function toQueryString($paramMap)
    {
        $StrQuery = "";
        foreach ($paramMap as $k => $v) {
            $StrQuery .= strlen($StrQuery) == 0 ? "" : "&";
            $StrQuery .= $k . "=" . $v;
        }
        return $StrQuery;
    }

    /**
     * RSA签名
     * @param $data 待签名数据
     * return 签名结果
     */
    function rsaSign($data,$type = 'RSA') {
        $private_key = "-----BEGIN RSA PRIVATE KEY-----\n" .
            wordwrap($this->selfConfig['private_key'], 64, "\n", true) .
            "\n-----END RSA PRIVATE KEY-----";
        extension_loaded('openssl') or die('php需要openssl扩展支持');
        $res=openssl_get_privatekey($private_key);
        if($res)
        {
            if($type == 'RSA'){
                openssl_sign($data, $sign,$res);
            }elseif($type == 'RSA2'){
                openssl_sign($data, $sign,$res,"SHA256");
            }
            openssl_free_key($res);
        }else {
            exit("私钥格式有误");
        }
        $sign = base64_encode($sign);
        return $sign;
    }

    /**
     * RSA验签
     * @param $data 待签名数据
     * @param $sign 要校对的的签名结果
     * return 验证结果
     */
    function rsaCheck($data, $sign,$type = 'RSA')  {
        $search = [
            "-----BEGIN PUBLIC KEY-----",
            "-----END PUBLIC KEY-----",
            "\n",
            "\r",
            "\r\n"
        ];
        $public_key= str_replace($search,"",$this->selfConfig['public_key']);
        $public_key=$search[0] . PHP_EOL . wordwrap($public_key, 64, "\n", true) . PHP_EOL . $search[1];
        $res=openssl_get_publickey($public_key);
        if($res)
        {
            if($type == 'RSA'){
                $result = (bool)openssl_verify($data, base64_decode($sign), $res);
            }elseif($type == 'RSA2'){
                $result = (bool)openssl_verify($data, base64_decode($sign), $res,OPENSSL_ALGO_SHA256);
            }
            openssl_free_key($res);
        }else{
            exit("公钥格式有误!");
        }
        return $result;
    }


    /**
     * 加密
     * */
    protected function encrypt($params = [])
    {
        $encryptStr = $this->crypt3DesEcb->encrypt(json_encode($params));

        return $encryptStr;
    }



    /**
     * 解密本地加密数据
     * */
    protected function decrypt($encryptStr = '')
    {
        $decrypt = $this->crypt3DesEcb->decrypt($encryptStr);
        if (is_string($decrypt)) {
            $decrypt = json_decode($decrypt, true);
        }

        return $decrypt;
    }

    /**
     * 生成签名
     * */
    protected function generateSign($encryptStr)
    {
        // 此处不要用 http_build_query() , 云账户Api接口局限字符硬编码

        $inputParams = 'data=' . $encryptStr . '&mess=' . $this->mess . '&timestamp=' . $this->timestamp . '&key=' . $this->selfConfig['appKey'];
        $signStr = hash_hmac('sha256', $inputParams, $this->selfConfig['appKey']);

        return $signStr;
    }

    /**
     * 请求格式化
     * */
    protected function curlFormat($params, $api, $requestId ,$method='post')
    {
        // 加密串
        $encryptStr = $this->encrypt($params);
        $request =[
            'data' => $encryptStr,
            'mess' => $this->mess,
            'timestamp' => $this->timestamp,
            'sign_type' => 'rsa',
        ];
        // 签名
        $signStr = $this->rsaSign($request ,'RSA');
        if($method == 'post'){
            $result = curlRequest(
                $this->selfConfig['domain'] . $this->subApi[$api],
                [
                    'data' => $encryptStr,
                    'mess' => $this->mess,
                    'timestamp' => $this->timestamp,
                    'sign' => $signStr,
                    'sign_type' => 'sha256',
                ],
                false,
                [
                    'dealer-id:' . $this->selfConfig['dealerId'],
                    'request-id:local-timestamp:' . $this->timestamp . '-' . $requestId,
                ]
            );
        }else{
            $result = $this->curl_request_get(
                $this->selfConfig['domain'] . $this->subApi[$api],
                [
                    'data' => $encryptStr,
                    'mess' => $this->mess,
                    'timestamp' => $this->timestamp,
                    'sign' => $signStr,
                    'sign_type' => 'sha256',
                ],
                [
                    'dealer-id:' . $this->selfConfig['dealerId'],
                    'request-id:local-timestamp:' . $this->timestamp . '-' . $requestId,
                ]
            );

        }


        return $result;
    }

    /**
     * 校验银行卡
     *
     * 银⾏卡三要素验证
     * */
    public function verifyBankCard()
    {
        // 请求ID
        $requestId = 'id-card-' . $this->requestData['idcard_number'];

        $params = [
            'card_no' => $this->requestData['bank_no'],// 银行卡号
            'id_card' => $this->requestData['idcard_number'],// 银行开户身份证
            'real_name' => $this->requestData['idcard_name'],// 银行卡开户姓名
        ];
        $data = $this->getRequestData($params);
        return curlRequest(
            $this->selfConfig['domain'] . $this->subApi['verifyBankCard'],
            $data,
            false,
            [
                'dealer-id:' . $this->selfConfig['dealerId'],
                'request-id:local-timestamp:' . $this->timestamp . '-' . $requestId,
            ]
        );
    }

    /**
     * 获取云账户签约协议预览链接
     *
     * */
    public function userContract()
    {
        // 请求ID
        $requestId = 'user-contract-' . time();

        $params = [
            'dealer_id' => $this->selfConfig['dealerId'],
            'broker_id' => $this->selfConfig['brokerId'],
        ];
        $data = $this->getRequestData($params);

        return  $this->curl_request_get(
            $this->selfConfig['domain'] . $this->subApi['userContract'],
            $data,
            [
                'dealer-id:' . $this->selfConfig['dealerId'],
                'request-id:local-timestamp:' . $this->timestamp . '-' . $requestId,
            ]
        );
    }


    /**
     * 查询订单状态
     * */
    public function queryOrderStatus($orderId)
    {


        $params = [
            'order_id' => $orderId,
        ];
        $data = $this->getRequestData($params);

        return  $this->curl_request_get(
            $this->selfConfig['domain'] . $this->subApi['queryOrderStatus'],
            [
                'data' => $data['data'],
                'mess' => $data['mess'],
                'timestamp' => $data['timestamp'],
                'sign' => $data['sign'],
                'sign_type' => 'rsa',
            ],
            [
                'dealer-id:' . $this->selfConfig['dealerId'],
                'request-id:local-timestamp:' . $this->timestamp . '-' . $orderId,
            ]
        );

    }


    /**
     * 打款接口
     *
     * 银行卡实时下单
     * */
    public function orderRealTime()
    {
        // 请求ID
        $requestId = 'order-id-' . $this->requestData['order_sn'];

        $params = [
            'order_id' => $this->requestData['order_sn'],
            'dealer_id' => $this->selfConfig['dealerId'],
            'broker_id' => $this->selfConfig['brokerId'],
            'real_name' => $this->requestData['owner_name'],// 银行卡开户姓名
            'card_no' => $this->requestData['bank_no'],// 银行卡号
            'id_card' => $this->requestData['idcard_number'],// 银行开户身份证
            'pay' => strval($this->requestData['price']),
            'anchor_id' => strval($this->requestData['user_id']),
            'pay_remark' => $this->requestData['pay_remark'],// 打款备注
            'notify_url' => $this->selfConfig['notifyUrl'],// 回调地址
        ];
        $data = $this->getRequestData($params);
        return curlRequest(
            $this->selfConfig['domain'] . $this->subApi['orderRealTime'],
            $data,
            false,
            [
                'dealer-id:' . $this->selfConfig['dealerId'],
                'request-id:local-timestamp:' . $this->timestamp . '-' . $requestId,
            ],
            30
        );
    }

    /**
     * 解密回调数据
     * */
    public function decryptNotifyData()
    {
        if (empty($this->requestData['data']) or !is_string($this->requestData['data'])) {
            return false;
        }

        $decryptData = $this->decrypt($this->requestData['data']);
        if (!is_array($decryptData)) {
            return false;
        }

        return $decryptData;
    }

    /**
     * 获取人脸识别地址
     * */
    public function getH5Url()
    {
        $this->requestData['userId'] = strval($this->requestData['userId']);
        // 请求ID
        $requestId = 'user-id-' . $this->requestData['userId'];

        $params = [
            'dealer_id' => $this->selfConfig['dealerId'],
            'broker_id' => $this->selfConfig['brokerId'],
            'dealer_user_id' => $this->requestData['userId'],
            'client_type' => 1,
            'return_url' => $this->requestData['returnUrl'],// 跳转页面地址
            'notify_url' => $this->requestData['notifyUrl'],// 回调地址
        ];
        write_log('applyWithdraw-cloudAccount' ,'getH5Url',$params);
        $data = $this->getRequestData($params);
        return $this->curl_request_get(
            $this->selfConfig['domain'] . $this->subApi['getH5Url'],
            $data ,
            [
                'dealer-id:' . $this->selfConfig['dealerId'],
                'request-id:local-timestamp:' . $this->timestamp . '-' . $requestId,
            ]
        );
    }

    /**
     * 查询个体工商户状态
     * */
    public function queryStatus()
    {
        $this->requestData['openid'] = strval($this->requestData['openid']);
        // 请求ID
        $requestId = 'open-id-' . $this->requestData['openid'];

        $params = [
            'dealer_id' => $this->selfConfig['dealerId'],
            'broker_id' => $this->selfConfig['brokerId'],
            'open_id' =>$this->requestData['openid']
        ];
        $data = $this->getRequestData($params);
        return $this->curl_request_get(
            $this->selfConfig['domain'] . $this->subApi['queryStatus'],
            $data ,
            [
                'dealer-id:' . $this->selfConfig['dealerId'],
                'request-id:local-timestamp:' . $this->timestamp . '-' . $requestId,
            ]
        );
    }





    public function __destruct()
    {
        // TODO: Implement __destruct() method.
    }

    function curl_request_get($action, $params,$header = [])
    {
        $ch = curl_init();
        $action .= strpos($action, '?') === false ? '?' : '&';
        $action .= build_query($params);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_URL, $action);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //处理http证书问题
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        curl_setopt($ch, CURLOPT_DNS_USE_GLOBAL_CACHE, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $ret = curl_exec($ch);
        if (false === $ret) {
            $ret = curl_errno($ch);
        }
        curl_close($ch);
        return $ret;
    }

    /**
     * 配置私钥
     * openssl_pkey_get_private这个函数可用来判断私钥是否是可用的，可用，返回资源
     * @return bool|resource
     */
    private function getPrivateKey()
    {

        $privateKey  = openssl_get_privatekey($this->selfConfig['private_key']);
        if(!$privateKey){
            die('私钥不可用');
        }
        return $privateKey;
    }


    /**
     * 配置公钥
     * openssl_pkey_get_public这个函数可用来判断私钥是否是可用的，可用，返回资源
     * @return resource
     */
    public function getPublicKey()
    {

        $publicKey = openssl_pkey_get_public($this->selfConfig['public_key']);
        if(!$publicKey){
            die('公钥不可用');
        }

        return  $publicKey ;
    }



    /**
     * 签名算法
     * @access public
     * @param $data
     * @return string
     */
    public function sign($data){
        $res=openssl_get_privatekey($this->getPrivateKey());
        if($res)
        {
            openssl_sign($data, $sign,$res,"SHA256");
            openssl_free_key($res);
        }else {
            exit("私钥格式有误");
        }
        $sign = base64_encode($sign);
        return $sign;

    }

    /**
     * 验签
     * @access public
     * @param $data
     * @return
     */
    public function verify($response){
        $signData = "data=".$response['data']."&mess=".$response['mess']."&timestamp=".$response['timestamp']."&key=".$this->config->app_key;
        //echo  $signData;
        $result = (bool)openssl_verify( $signData, base64_decode($response['sign']), $this->getPublicKey(),"SHA256");
        return $result;
    }

    /**
     * 私钥解密   用于个税下载、发票下载文件解压密码的解密
     */
    public function privateDecrypt($data)
    {
        openssl_private_decrypt(base64_decode($data),$decrypted, $this->getPrivateKey());

        return  $decrypted;
    }

    protected function getRequestData($params)
    {
        $desData = $this->encrypt($params);
        $signData = "data=".$desData."&mess=".$this->mess."&timestamp=".$this->timestamp."&key=".$this->selfConfig['appKey'];
        $sign = $this->sign($signData);
        $postData              = [];
        $postData['data']      = $desData;
        $postData['mess']      = $this->mess;
        $postData['timestamp'] = $this->timestamp;
        $postData['sign']      = $sign;
        $postData['sign_type'] = 'rsa';
        return $postData;
    }
}
